package org.jeecg.modules.srs.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.RestUtil;
import org.jeecg.modules.interfaceMapping.entity.InterfaceMapping;
import org.jeecg.modules.interfaceMapping.service.IInterfaceMappingService;
import org.jeecg.modules.videoLog.entity.VideoLog;
import org.jeecg.modules.videoLog.service.IVideoLogService;
import org.jeecg.modules.videoMapping.entity.VideoMapping;
import org.jeecg.modules.videoMapping.service.IVideoMappingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.http.ResponseEntity.BodyBuilder;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;


@Api(tags = "视频查看")
@RestController
@RequestMapping("/api")
@Slf4j
public class VideoController {

    @Autowired
    private IVideoMappingService videoMappingService;
    @Autowired
    private IInterfaceMappingService interfaceMappingService;
    @Autowired
    private IVideoLogService videoLogService;
    @Autowired
    private RedisUtil redisUtil;

    private static final String PLAYBACK_KEY = "playback:";

    private static final String TOKEN = "HM3SOlbpH71yEXUI1736838206014623";
    private static final String PLAY_URL = "http://121.199.49.186:8060/sip/play/start";
    private static final String REPLAY_URL = "http://121.199.49.186:8060/sip/replay/start";

    @AutoLog(value = "市级-视频查看")
    @ApiOperation(value = "市级-视频查看", notes = "市级-视频查看")
    @GetMapping(value = "/video/{location}/{channel}")
    public ResponseEntity<StreamingResponseBody> getVideo(
            @PathVariable String location,
            @PathVariable String channel,
            @RequestParam(name = "access_token", required = false) String accessToken,
            @RequestParam(name = "starttime", required = false) String startTime,
            @RequestParam(name = "endtime", required = false) String endTime) {
        if (StringUtils.isEmpty(accessToken)) {
            log.error("Access token 为空");
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        VideoLog videoLog = new VideoLog();
        videoLog.setLocation(location).setChannel(channel).setAccessToken(accessToken)
                .setStartTime(startTime).setEndTime(endTime);
        // 视频映射查找
        VideoMapping videoMapping = videoMappingService.getByLocationAndChannel(location, channel);
        if (videoMapping == null) {
            log.warn("未找到正确的视频地址映射 location: {} and channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        // 访问令牌验证
        String company = videoMapping.getCompany();
        InterfaceMapping interfaceMapping = interfaceMappingService.getUrlByLocation(company);
        String baseUrl = interfaceMapping.getBaseUrl();
        ResponseEntity<String> res = RestUtil.request(baseUrl + "/networking/networking/getToken", HttpMethod.GET, null, null, null, String.class);
        String body = res.getBody();
        if (!accessToken.equals(body)){
            log.error("Access token 验证失败. 正确: {}, 参数: {}", body, accessToken);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }
        // 视频URL构建
        String videoUrl;
        if (startTime != null && endTime != null) {
            saveToRedis(location, channel, startTime, endTime);
            videoUrl = REPLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN
                    + "&start=" + convertToTimestamp(startTime) + "&stop=" + convertToTimestamp(endTime)
                    + "&option=4";
        } else {
            videoUrl = PLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN;
        }
        if (StringUtils.isEmpty(videoUrl)) {
            log.error("视频地址为空, location: {} and channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        videoLog.setVideoUrl(videoUrl);
        String flvUrl = getFlvUrl(videoUrl, 5, 1000); // 最多重试5次，初始等待时间为1秒
        if (StringUtils.isEmpty(flvUrl)) {
            log.error("未能获取有效的FLV URL, location: {}, channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        try {
            URL url = new URL(flvUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            int responseCode = connection.getResponseCode();
            videoLog.setResponseCode(responseCode);
            if (responseCode >= 200 && responseCode < 300) {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.parseMediaType("video/x-flv"));
                headers.setContentLength(connection.getContentLength());
                return ResponseEntity.ok()
                        .headers(headers)
                        .body(outputStream -> {
                            try (InputStream inputStream = connection.getInputStream()) {
                                byte[] buffer = new byte[8192]; // 增加缓冲区大小以提高传输效率
                                int bytesRead;
                                long totalBytesRead = 0;
                                while ((bytesRead = inputStream.read(buffer)) != -1) {
                                    outputStream.write(buffer, 0, bytesRead);
                                    totalBytesRead += bytesRead;
                                }
                                videoLog.setTotalBytesRead(String.valueOf(totalBytesRead));
                            } catch (IOException e) {
                                log.error("读取视频流时出错.", e);
                                videoLog.setException(e.getMessage());
                            } finally {
                                videoLogService.save(videoLog);
                            }
                        });
            } else {
                log.error("无法连接到视频流。HTTP状态: {}", responseCode);
                videoLogService.save(videoLog);
                return ResponseEntity.status(responseCode).build();
            }
        } catch (Exception e) {
            log.error("尝试连接到视频流或从视频流读取时出错", e);
            videoLog.setException(e.getMessage());
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    private String getFlvUrl(String videoUrl, int maxRetries, long initialDelayMillis) {
        int attempt = 0;
        long delayMillis = initialDelayMillis;

        while (attempt < maxRetries) {
            try {
                JSONObject jsonObject = RestUtil.get(videoUrl);
                if (jsonObject.getInteger("code") == 0) {
                    JSONObject data = jsonObject.getJSONObject("data");
                    return "http://" + data.getString("http") + data.getString("flv"); // 确保URL完整
                } else {
                    log.warn("获取FLV URL失败，尝试 {} 次后将重试。API响应: {}", attempt + 1, jsonObject.toJSONString());
                }
            } catch (Exception e) {
                log.error("获取FLV URL时出错，尝试 {} 次后将重试", attempt + 1, e);
            }

            // Wait before the next retry, with exponential backoff
            try {
                TimeUnit.MILLISECONDS.sleep(delayMillis);
                delayMillis *= 2; // Double the delay for the next attempt
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                log.error("线程在等待重试时被中断", ie);
                break;
            }

            attempt++;
        }

        log.error("达到最大重试次数，未能成功获取FLV URL.");
        return null;
    }

    @AutoLog(value = "王工-视频查看-不校验token")
    @ApiOperation(value = "王工-视频查看-不校验token", notes = "王工-视频查看-不校验token")
    @GetMapping(value = "/getVideo/{location}/{channel}")
    public ResponseEntity<StreamingResponseBody> video(
            @PathVariable String location,
            @PathVariable String channel,
            @RequestParam(name = "starttime", required = false) String startTime,
            @RequestParam(name = "endtime", required = false) String endTime) {
        VideoLog videoLog = new VideoLog();
        videoLog.setLocation(location).setChannel(channel)
                .setStartTime(startTime).setEndTime(endTime);
        // 视频映射查找
        VideoMapping videoMapping = videoMappingService.getByLocationAndChannel(location, channel);
        if (videoMapping == null) {
            log.warn("未找到正确的视频地址映射 location: {} and channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        // 视频URL构建
        String videoUrl;
        if (startTime != null && endTime != null) {
            saveToRedis(location, channel, startTime, endTime);
            videoUrl = REPLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN
                    + "&start=" + convertToTimestamp(startTime) + "&stop=" + convertToTimestamp(endTime)
                    + "&option=4";
        } else {
            videoUrl = PLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN;
        }
        if (StringUtils.isEmpty(videoUrl)) {
            log.error("视频地址为空, location: {} and channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        videoLog.setVideoUrl(videoUrl);
        String flvUrl = getFlvUrl(videoUrl, 5, 1000); // 最多重试5次，初始等待时间为1秒
        if (StringUtils.isEmpty(flvUrl)) {
            log.error("未能获取有效的FLV URL, location: {}, channel: {}", location, channel);
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
        }
        try {
            URL url = new URL(flvUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            int responseCode = connection.getResponseCode();
            videoLog.setResponseCode(responseCode);
            if (responseCode >= 200 && responseCode < 300) {
                InputStream inputStream = connection.getInputStream();
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.parseMediaType("video/x-flv"));
                headers.setContentLength(connection.getContentLength());
                return ResponseEntity.ok()
                        .headers(headers)
                        .body(outputStream -> {
                            byte[] buffer = new byte[1024];
                            int bytesRead;
                            long totalBytesRead = 0;
                            try {
                                while ((bytesRead = inputStream.read(buffer)) != -1) {
                                    outputStream.write(buffer, 0, bytesRead);
                                    totalBytesRead += bytesRead;
                                }
                            } catch (Exception e) {
                                log.error("读取视频流时出错.", e);
                                videoLog.setException(e.getMessage());
                            } finally {
                                videoLog.setTotalBytesRead(String.valueOf(totalBytesRead));
                                videoLogService.save(videoLog);
                                inputStream.close();
                            }
                        });
            } else {
                log.error("无法连接到视频流。HTTP状态: {}", responseCode);
                videoLogService.save(videoLog);
                return ResponseEntity.status(responseCode).build();
            }
        } catch (Exception e) {
            log.error("尝试连接到视频流或从视频流读取时出错", e);
            videoLog.setException(e.getMessage());
            videoLogService.save(videoLog);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @AutoLog(value = "区级-视频查看")
    @ApiOperation(value = "区级-视频查看", notes = "区级-视频查看")
    @GetMapping(value = "/play/start/{nationalStandardCode}/{channelCode}")
    public JSONObject getVideoNoToken(
            @PathVariable String nationalStandardCode,
            @PathVariable String channelCode) {
        JSONObject res = new JSONObject();
        JSONObject data = new JSONObject();
        JSONObject tracks = new JSONObject();
        tracks.put("fps", 25);
        tracks.put("height", 720);
        tracks.put("width", 1280);
        int code;
        String msg;
        // 视频映射查找
        VideoMapping videoMapping = videoMappingService.getByChannelCode(channelCode);
        if (videoMapping != null) {
            // 视频URL构建
            String videoUrl=  PLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN;
            String flvUrl = getFlvUrl(videoUrl, 5, 1000); // 最多重试5次，初始等待时间为1秒
            code = 0;
            msg = "成功";
            data.put("tracks", tracks);
            data.put("flv", flvUrl);
        } else {
            log.warn("未找到正确的视频地址映射 channelCode: {}", channelCode);
            code = 404;
            msg = "url丢失啦";
        }
        res.put("code", code);
        res.put("msg", msg);
        res.put("data", data);
        return res;
    }

    @AutoLog(value = "区级-视频回放")
    @ApiOperation(value = "区级-视频回放", notes = "区级-视频回放")
    @GetMapping(value = "/play/recordstart/{nationalStandardCode}/{channelCode}/{starttime}")
    public JSONObject getRecordStart(@PathVariable String nationalStandardCode,
                                     @PathVariable String channelCode,
                                     @PathVariable long starttime){
        JSONObject res = new JSONObject();
        JSONObject data = new JSONObject();
        JSONObject tracks = new JSONObject();
        tracks.put("fps", 25);
        tracks.put("height", 720);
        tracks.put("width", 1280);
        int code;
        String msg;
        // 视频映射查找
        VideoMapping videoMapping = videoMappingService.getByChannelCode(channelCode);
        if (videoMapping != null) {
            // 视频URL构建
            long endTime = starttime + 30 * 60;
            String videoUrl= REPLAY_URL + "?deviceid=" + videoMapping.getDeviceId()
                    + "&channelid=" + videoMapping.getChannelId() + "&Authorization=" + TOKEN
                    + "&start=" + starttime + "&stop=" + endTime
                    + "&option=4";
            String flvUrl = getFlvUrl(videoUrl, 5, 1000); // 最多重试5次，初始等待时间为1秒
            String location = videoMapping.getLocation();
            String channel = videoMapping.getChannel();
            saveToRedis(location, channel, formatUnixTimestamp(starttime), formatUnixTimestamp(endTime));
            code = 0;
            msg = "成功";
            data.put("tracks", tracks);
            data.put("flv", flvUrl);
        } else {
            log.error("未找到正确的视频地址映射 channelCode: {}", channelCode);
            code = 404;
            msg = "url丢失啦";
        }
        res.put("code", code);
        res.put("msg", msg);
        res.put("data", data);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
        return res;
    }

    @ApiOperation(value = "获取回放任务", notes = "获取回放任务")
    @GetMapping("/video/getPlaybackTask")
    public Result<?> getPlaybackTask(@RequestParam(name = "location") String location,
                                     @RequestParam(name = "channel") String channel){
        List<JSONObject> list = new ArrayList<>();
        for (String s : channel.split(",")) {
            String key = PLAYBACK_KEY + location + s;
            if (redisUtil.hasKey(key)) {
                String o = (String) redisUtil.get(key);
                JSONObject jsonObject = JSONObject.parseObject(o);
                list.add(jsonObject);
                redisUtil.del(key);
            }
        }
        return Result.OK(list);
    }

    @AutoLog(value = "保存回放任务")
    @ApiOperation(value = "保存回放任务", notes = "保存回放任务")
    @GetMapping("/video/saveToRedis")
    public Result<?> save(@RequestParam String location,
                          @RequestParam String channel,
                          @RequestParam String startTime,
                          @RequestParam String endTime){
        saveToRedis(location, channel, startTime, endTime);
        return Result.OK();
    }


    public void saveToRedis(String location, String channel, String startTime, String endTime) {
        String key = PLAYBACK_KEY + location + channel;
        long l = calculateTimeDifferenceInSeconds(startTime, endTime);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("location", location);
        jsonObject.put("channel", channel);
        jsonObject.put("startTime", convertTimeFormat(startTime));
        jsonObject.put("endTime", convertTimeFormat(endTime));
        jsonObject.put("difference", String.valueOf(l));
        redisUtil.set(key, jsonObject.toJSONString());
    }

    public static long calculateTimeDifferenceInSeconds(String startTime, String endTime){
        // 定义日期格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd't'HHmmss'z'");
        try {
            // 解析时间字符串
            Date date1 = dateFormat.parse(startTime);
            Date date2 = dateFormat.parse(endTime);
            // 计算时间差（以毫秒为单位）
            long differenceInMillis = Math.abs(date2.getTime() - date1.getTime());
            // 转换为秒
            return differenceInMillis / 1000;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static String convertTimeFormat(String originalTime) {
        // 定义原始日期格式
        SimpleDateFormat originalFormat = new SimpleDateFormat("yyyyMMdd't'HHmmss'z'");
        // 定义目标日期格式
        SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            // 解析原始时间字符串
            Date date = originalFormat.parse(originalTime);
            // 格式化为目标时间字符串
            return targetFormat.format(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    public static long convertToTimestamp(String timeString) {
        // 假设输入的时间字符串是基于UTC+8 (CST)
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd't'HHmmss'z'");
        // 设置解析时使用的时区为中国标准时间（UTC+8）
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 或者使用 "GMT+8"

        try {
            Date date = sdf.parse(timeString);
            // 将毫秒时间戳转换为秒，并取整
            return date.getTime() / 1000;
        } catch (ParseException e) {
            // 处理解析异常
            throw new IllegalArgumentException("无法解析时间字符串: " + timeString, e);
        }
    }

    /**
     * 将Unix时间戳转换为指定格式的字符串。
     *
     * @param timestamp Unix时间戳（以毫秒为单位）
     * @return 格式化后的日期字符串
     */
    public static String formatUnixTimestamp(long timestamp) {
        // 创建一个Instant对象
        Instant instant = Instant.ofEpochSecond(timestamp);
        // 定义日期格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd't'HHmmss'z'")
                .withZone(ZoneId.systemDefault());
        // 使用格式化工具将日期转换为字符串
        return formatter.format(instant);
    }

    public static String getMD5String(String str) {
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(str.getBytes());
            // digest()最后确定返回md5 hash值，返回值为8位字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
            //一个byte是八位二进制，也就是2位十六进制字符（2的8次方等于16的2次方）
            return new BigInteger(1, md.digest()).toString(16);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return null;
        }
    }

    public static void main(String[] args) {
        String videoUrl = "http://121.199.49.186:8060/sip/replay/start?deviceid=34020000001180000006&channelid=34020000001320000001" +
                "&Authorization=HM3SOlbpH71yEXUI1736838206014623&start=1736874720&stop=1736874820&option=4";
//        String videoUrl = "http://121.199.49.186:8090/34020000001180000006/34020000001180000006_34020000001320000004_10_1736870400_1736872056_HM3SOlbpH71yEXUI1736838206014623.live.flv";
//        JSONObject jsonObject = RestUtil.get(videoUrl);
//        log.info("--------------------------------------------------------------------------------");
//        log.info(jsonObject.toJSONString());
//        log.info("--------------------------------------------------------------------------------");
        try {
            URL url = new URL(videoUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");

            connection.setConnectTimeout(5000); // 设置连接超时时间
            connection.setReadTimeout(5000); // 设置读取超时时间

            int responseCode = connection.getResponseCode();
            if (responseCode >= 200 && responseCode < 300) {
                log.info("请求成功");

                // 尝试读取响应头信息
                log.info("Content-Type: {}", connection.getContentType());
                log.info("Content-Length: {}", connection.getContentLength());

                // 如果你想验证是否有实际的数据流，可以尝试读取一部分响应体
                try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
                    StringBuilder content = new StringBuilder();
                    String line;
                    int bytesRead = 0;
                    while ((line = in.readLine()) != null && bytesRead < 1024) { // 只读取前1KB的数据
                        content.append(line);
                        bytesRead += line.length();
                    }
                    log.info("部分响应内容: {}", content.toString());
                }

                log.info("视频流请求成功并接收到数据");
            } else {
                log.error("无法连接到视频流。HTTP状态: {}", responseCode);
            }
        } catch (Exception e) {
            log.error("尝试连接到视频流或从视频流读取时出错", e);
        }
    }

}
